using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace Hack.Decompiling.PEA
{
  public static unsafe class Utils
  {
    public static readonly DateTime BaseDateTime = new DateTime(1970,1,1);

    public static readonly string BreakLine = new string('*', 80) +"\r\n";

    public static int GetTimeStamp(DateTime dateTime)
    {
      return dateTime.Subtract(BaseDateTime).Seconds;
    }

    public static DateTime GetDateTime(int timeStamp)
    {
      return BaseDateTime.AddSeconds(timeStamp);
    }
    #region Aligns
    public static UInt32 Align(UInt32 address,UInt32 block)
    {
      UInt32 remainder = address % block;
      return remainder == 0 ? address : address + (block - remainder);
    }
    public static Byte[] AlignBytes2(Byte[] bytes)
    {
      return AlignBytes(bytes, 2);
    }
    public static Byte[] AlignBytes4(Byte[] bytes)
    {
      return AlignBytes(bytes, 4);
    }
    public static Byte[] AlignBytes(Byte[] bytes, UInt32 align)
    {
      if (bytes != null)
      {
        UInt32 remainder = (UInt32)(bytes.Length % align);
        //stubs should always contain even length of bytes.(2x)
        if (remainder != 0)
        {
          Byte[] aligned = new Byte[bytes.Length + (align - remainder)];

          if (aligned != null)
          {
            bytes.CopyTo(aligned, 0);

            bytes = aligned;
          }
        }
      }
      return bytes;
    }
    #endregion
    #region Names
    public static string ReadName(BinaryReader reader, Encoding encoding, UInt32 filePointer)
    {
      string name = string.Empty;
      if (reader != null && filePointer != 0)
      {
        Int64 current = Utils.GetPosition(reader);
        if (Utils.SetPosition(reader, filePointer))
        {
          name = ReadName(reader, encoding);
          Utils.SetPosition(reader, current);
        }
      }
      return name;
    }
    public static string ReadName(BinaryReader reader, Encoding encoding)
    {
      string name = string.Empty;
      if (reader != null)
      {
        encoding = encoding == null ? Encoding.ASCII : encoding;
        List<Byte> bytes = new List<Byte>();

        Byte c = (Byte)'\0';
        while (true)
        {
          c = (Byte)reader.ReadByte();
          if (c != (Byte)'\0')
          {
            bytes.Add(c);
          }
          else
          {
            break;
          }
        }
        name = encoding.GetString(bytes.ToArray());
      }
      return name;
    }
    public static string WriteName(BinaryWriter writer, UInt32 filePointer, string name, Encoding encoding)
    {
      if (writer != null && filePointer != 0)
      {
        Int64 current = Utils.GetPosition(writer);
        if (Utils.SetPosition(writer, filePointer))
        {
          WriteName(writer, name, encoding);
          Utils.SetPosition(writer, current);
        }
      }
      return name;
    }
    public static string WriteName(BinaryWriter writer, string name, Encoding encoding)
    {
      if (writer != null && name != null)
      {
        encoding = encoding == null ? Encoding.ASCII : encoding;

        Byte[] bytes = encoding.GetBytes(name);

        for (int i = 0; i < bytes.Length; i++)
        {
          writer.Write(bytes[i]);
        }
        writer.Write((Byte)'\0');
      }
      return name;
    }
    #endregion
    #region Length
    public static Int64 GetLength(Stream stream)
    {
      return stream!=null ? stream.Length : 0;
    }
    public static Int64 GetLength(BinaryReader reader)
    {
      return reader != null ? GetLength(reader.BaseStream) : 0;
    }
    public static Int64 GetLength(BinaryWriter writer)
    {
      return writer != null ? GetLength(writer.BaseStream) : 0;
    }
    #endregion
    #region Position
    public static Int64 GetPosition(Stream stream)
    {
      return stream != null ? stream.Position : 0;
    }
    public static Int64 GetPosition(BinaryReader reader)
    {
      return reader != null ? GetPosition(reader.BaseStream) : 0;
    }
    public static Int64 GetPosition(BinaryWriter writer)
    {
      return writer != null ? GetPosition(writer.BaseStream) : 0;
    }
    public static Boolean SetPosition(Stream stream,Int64 position)
    {
      bool canSeek = false;
      if (stream != null)
      {
        if (canSeek = stream.CanSeek)
        {
          stream.Position = position;
        }
      }
      return canSeek;
    }
    public static Boolean SetPosition(BinaryReader reader, Int64 position)
    {
      return reader != null ? SetPosition(reader.BaseStream, position) : false;
    }
    public static Boolean SetPosition(BinaryWriter writer, Int64 position)
    {
      return writer != null ? SetPosition(writer.BaseStream, position) : false;
    }
    #endregion
    #region Read/Write
    public static Byte[] ReadBytes(BinaryReader reader, UInt32 n)
    {
      return reader != null ? reader.ReadBytes((Int32)n) : null;
    }
    public static Byte* Read8(BinaryReader reader, Byte* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        buffer[i] = Utils.Read8(reader);
      }
      return buffer;
    }
    public static Byte Read8(BinaryReader reader)
    {
      return reader != null ? reader.ReadByte() : (Byte)0;
    }
    public static UInt16* Read16(BinaryReader reader, UInt16* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        buffer[i] = Utils.Read16(reader);
      }
      return buffer;
    }
    public static UInt16 Read16(BinaryReader reader)
    {
      return reader != null ? reader.ReadUInt16() : (UInt16)0;
    }
    public static UInt32 Read32(BinaryReader reader)
    {
      return reader != null ? reader.ReadUInt32() : 0;
    }
    public static UInt64 Read64(BinaryReader reader)
    {
      return reader != null ? reader.ReadUInt64() : 0;
    }
    public static UInt64* Read64(BinaryReader reader, UInt64* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        buffer[i] = Utils.Read64(reader);
      }
      return buffer;
    }

    public static Byte[] WriteBytes(BinaryWriter writer, Byte[] bytes)
    {
      if (bytes != null && writer != null)
      {
        writer.Write(bytes);
      }
      return bytes;
    }
    public static Byte* Write8(BinaryWriter writer, Byte* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        Utils.Write8(writer, buffer[i]);
      }
      return buffer;
    }
    public static Byte Write8(BinaryWriter writer, Byte _byte)
    {
      if (writer != null)
      {
        writer.Write(_byte);
      }
      return _byte;
    }
    public static UInt16* Write16(BinaryWriter writer, UInt16* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        Utils.Write16(writer, buffer[i]);
      }
      return buffer;
    }
    public static UInt64* Write64(BinaryWriter writer, UInt64* buffer, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        Utils.Write64(writer, buffer[i]);
      }
      return buffer;
    }
    public static UInt16 Write16(BinaryWriter writer, UInt16 word)
    {
      if (writer != null)
      {
        writer.Write(word);
      }
      return word;
    }
    public static UInt32 Write32(BinaryWriter writer, UInt32 dword)
    {
      if (writer != null)
      {
        writer.Write(dword);
      }
      return dword;
    }
    public static UInt64 Write64(BinaryWriter writer, UInt64 qword)
    {
      if (writer != null)
      {
        writer.Write(qword);
      }
      return qword;
    }
    #endregion
    #region Copy
    public static UInt32 CopyFixedArray(Byte* source, Byte* destination, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        destination[i] = source[i];
      }
      return n;
    }
    public static UInt32 CopyFixedArray(UInt16* source, UInt16* destination, UInt32 n)
    {
      for (UInt32 i = 0; i < n; i++)
      {
        destination[i] = source[i];
      }
      return n;
    }
    #endregion
  }
}
